package com.example.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.example.domain.entity.AutoScheduleSetting;
import com.example.domain.entity.Person;
import com.example.domain.entity.Schedule;
import com.example.mapper.PersonMapper;
import com.example.mapper.ScheduleMapper;
import com.example.service.IScheduleService;
import com.example.domain.query.ScheduleQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import java.time.LocalDate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author baomidou
 * @since 2025-05-14
 */
@Service
public class ScheduleServiceImpl extends ServiceImpl<ScheduleMapper, Schedule> implements IScheduleService {
    
    @Autowired
    private PersonMapper personMapper;

    @Override
    public IPage<Schedule> pageQuery(ScheduleQuery query, int pageNum, int pageSize) {
        Page<Schedule> page = new Page<>(pageNum, pageSize);
        QueryWrapper<Schedule> wrapper = new QueryWrapper<>();
        
        if (query != null) {
            if (query.getScheduleId() != null) {
                wrapper.eq("schedule_id", query.getPersonId());
            }
            if (query.getDeptId() != null) {
                wrapper.eq("dept_id", query.getDeptId());
            }
            if (query.getPersonId() != null) {
                wrapper.eq("person_id", query.getPersonId());
            }
            if (query.getStartDate() != null && query.getEndDate() != null) {
                wrapper.between("date", query.getStartDate(), query.getEndDate());
            }
            if (query.getShift() != null) {
                wrapper.eq("shift", query.getShift());
            }
            if (query.getIsLeave() != null) {
                wrapper.eq("is_leave", query.getIsLeave());
            }
        }
        
        return this.page(page, wrapper);
    }

    @Override
    public List<Schedule> getSchedules(LocalDate date, String range, Integer personId) {
        QueryWrapper<Schedule> wrapper = new QueryWrapper<>();
        
        // 根据range参数设置日期范围
        switch (range.toLowerCase()) {
            case "day":
                wrapper.eq("date", date);
                break;
            case "week":
                LocalDate weekStart = date.minusDays(date.getDayOfWeek().getValue() - 1);
                LocalDate weekEnd = weekStart.plusDays(6);
                wrapper.between("date", weekStart, weekEnd);
                break;
            case "month":
                LocalDate monthStart = date.withDayOfMonth(1);
                LocalDate monthEnd = date.withDayOfMonth(date.lengthOfMonth());
                wrapper.between("date", monthStart, monthEnd);
                break;
            default:
                throw new IllegalArgumentException("不支持的范围类型: " + range);
        }

        // 添加人员查询条件
        if (personId != null) {
            wrapper.eq("person_id", personId);
        }

        wrapper.orderByAsc("date");
        return this.list(wrapper);
    }

    @Override
    public String autoSchedule(AutoScheduleSetting setting) {
        // 验证参数
        if (setting.getStartDay() == null || setting.getEndDay() == null) {
            return "开始日期和结束日期不能为空";
        }
        if (setting.getStartDay().isAfter(setting.getEndDay())) {
            return "开始日期不能晚于结束日期";
        }
        if (setting.getWorkDay() == null || setting.getRestDay() == null || setting.getWorkDay() <= 0 || setting.getRestDay() < 0) {
            return "工作日和休息日设置无效";
        }
        if (setting.getShiftNum() == null || setting.getShiftNum() <= 0) {
            return "每班人数设置无效";
        }
        if (setting.getDeptId() == null) {
            return "部门ID不能为空";
        }
        if (setting.getPersonalShiftNum() == null || setting.getPersonalShiftNum() <= 0 || setting.getPersonalShiftNum() > 3) {
            return "每人每天值班次数设置无效";
        }
        
        // 获取需要排班的人员列表
        List<Person> personList = setting.getPersonList();
        if (personList == null || personList.isEmpty()) {
            // 如果未提供人员列表，则获取该部门所有人员
            QueryWrapper<Person> wrapper = new QueryWrapper<>();
            wrapper.eq("dept_id", setting.getDeptId())
                    .eq("title", "普通操作人员");
            personList = personMapper.selectList(wrapper);
            
            if (personList.isEmpty()) {
                return "未找到可排班人员";
            }
        }
        
        // 班次列表：早班、中班、晚班
        String[] shifts = {"早", "中", "晚"};
        
        // 计算需要排班的总天数
        LocalDate currentDate = setting.getStartDay();
        LocalDate endDate = setting.getEndDay();
        
        // 清除该时间段内该部门的现有排班
        QueryWrapper<Schedule> clearWrapper = new QueryWrapper<>();
        clearWrapper.eq("dept_id", setting.getDeptId())
                   .between("date", currentDate, endDate);
        this.remove(clearWrapper);
        
        // 开始自动排班
        int personCount = personList.size();
        int cycleLength = setting.getWorkDay() + setting.getRestDay(); // 一个完整的工作-休息周期长度
        int dayCount = 0; // 当前处理的是周期内的第几天
        
        // 为每个人创建初始的周期起点（错开每个人的工作周期起点，使人员分布更均匀）
        int[] personCycleStart = new int[personCount];
        for (int i = 0; i < personCount; i++) {
            personCycleStart[i] = (i * cycleLength / personCount) % cycleLength;
        }
        
        // 按日期逐天排班
        while (!currentDate.isAfter(endDate)) {
            // 记录每个人当天已经排了几个班次
            Map<Integer, Integer> personDailyShiftCount = new HashMap<>();
            
            // 对每个班次进行排班
            for (String shift : shifts) {
                int assignedCount = 0; // 当前班次已分配的人数
                
                // 遍历所有人员，决定是否安排工作
                for (int i = 0; i < personCount && assignedCount < setting.getShiftNum(); i++) {
                    Person person = personList.get(i);
                    
                    // 计算当前人员在周期中的位置
                    int personDayInCycle = (dayCount + personCycleStart[i]) % cycleLength;
                    
                    // 获取该人员当天已排班次数
                    int currentPersonShifts = personDailyShiftCount.getOrDefault(person.getPersonId(), 0);
                    
                    // 如果是工作日且未超过每人每天值班次数限制，则安排工作
                    if (personDayInCycle < setting.getWorkDay() && currentPersonShifts < setting.getPersonalShiftNum()) {
                        Schedule schedule = new Schedule();
                        schedule.setDeptId(setting.getDeptId());
                        schedule.setPersonId(person.getPersonId());
                        schedule.setDate(currentDate);
                        schedule.setShift(shift);
                        schedule.setIsLeave(false);
                        schedule.setLeaveApproved(false);
                        
                        // 保存排班记录
                        this.save(schedule);
                        assignedCount++;
                        
                        // 更新该人员当天已排班次数
                        personDailyShiftCount.put(person.getPersonId(), currentPersonShifts + 1);
                    }
                }
            }
            
            // 移动到下一天
            currentDate = currentDate.plusDays(1);
            dayCount++;
        }
        
        return "自动排班成功，共排班" + dayCount + "天";
    }

    @Override
    public boolean applyLeave(Schedule schedule) {
        // 检查请假信息是否完整
        if (schedule.getScheduleId() == null || schedule.getSubstitutePersonId() == null) {
            return false; // 信息不完整
        }

        // 查找对应的排班记录
        Schedule existingSchedule = this.getById(schedule.getScheduleId());

        if (existingSchedule == null) {
            return false; // 未找到对应的排班记录
        }

        // 更新请假状态和替班人
        existingSchedule.setIsLeave(true);
        existingSchedule.setLeaveApproved(false); // 默认未审核
        existingSchedule.setSubstitutePersonId(schedule.getSubstitutePersonId());

        return this.updateById(existingSchedule);
    }

    @Override
    public boolean reviewLeave(Integer scheduleId, Boolean approved) {
        // 查找请假记录
        Schedule schedule = this.getById(scheduleId);

        if (schedule == null || !schedule.getIsLeave()) {
            return false; // 未找到请假记录或该记录不是请假
        }

        // 更新审核状态
        schedule.setLeaveApproved(approved);

        return this.updateById(schedule);
    }
}
